package com.coderworm.netty;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.zip.CRC32;

public class RtcmFileHandler extends ChannelInboundHandlerAdapter {
    private final String saveDir;
    // 状态变量：记录文件接收阶段（0=文件名长度，1=文件名，2=文件大小，3=CRC，4=文件内容）
    private int fileStage = 0;
    private int fileNameLen;
    private String fileName;
    private long fileTotalSize;
    private long expectedCrc;
    private long receivedSize;
    private RandomAccessFile file;
    private CRC32 crc32;

    public RtcmFileHandler(String saveDir) {
        this.saveDir = saveDir;
        // 初始化保存目录（与原逻辑一致）
        new File(saveDir).mkdirs();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        try {
            while (buf.isReadable()) {
                switch (fileStage) {
                    // 阶段0-3：读取文件元数据（与原逻辑一致，异步处理数据不足场景）
                    case 0: // 文件名长度（2字节）
                        if (buf.readableBytes() < 2) return;
                        fileNameLen = buf.readShort();
                        fileStage = 1;
                        break;
                    case 1: // 文件名（fileNameLen字节）
                        if (buf.readableBytes() < fileNameLen) return;
                        byte[] fileNameBytes = new byte[fileNameLen];
                        buf.readBytes(fileNameBytes);
                        fileName = new String(fileNameBytes, StandardCharsets.UTF_8);
                        fileStage = 2;
                        break;
                    case 2: // 文件大小（8字节）
                        if (buf.readableBytes() < 8) return;
                        fileTotalSize = buf.readLong();
                        // 初始化文件与CRC（准备接收内容）
                        File saveFile = new File(saveDir + fileName);
                        // 避免文件覆盖，添加时间戳后缀（可选优化）
                        if (saveFile.exists()) {
                            fileName = fileName.replace(".bin", "_" + System.currentTimeMillis() + ".bin");
                            saveFile = new File(saveDir + fileName);
                        }
                        file = new RandomAccessFile(saveFile, "rw");
                        crc32 = new CRC32();
                        receivedSize = 0;
                        fileStage = 3;
                        System.out.println("开始接收文件：" + fileName + "，预期大小：" + fileTotalSize + "字节");
                        break;
                    case 3: // CRC校验码（4字节）
                        if (buf.readableBytes() < 4) return;
                        expectedCrc = buf.readInt() & 0xFFFFFFFFL;
                        fileStage = 4;
                        break;
                    case 4: // 接收文件内容（核心优化：零拷贝写入）
                        // 1. 校验RTCM帧头（仅第一次读取时校验）
                        if (receivedSize == 0) {
                            if (buf.readableBytes() < 1) return;
                            byte header = buf.readByte();
                            if (header != (byte) 0xD3) {
                                String errorMsg = "ERROR: 非法RTCM帧头（实际：0x" + Integer.toHexString(header & 0xFF).toUpperCase() + "，预期：0xD3）";
                                ctx.writeAndFlush(ctx.alloc().buffer().writeBytes(errorMsg.getBytes(StandardCharsets.UTF_8)));
                                ctx.close();
                                return;
                            }
                            // 帧头回写（计入文件与CRC）
                            file.write(header);
                            crc32.update(header);
                            receivedSize++;
                            System.out.println("文件帧头校验通过，开始接收文件内容");
                        }

                        // 2. 读取数据并写入文件（计算本次可读取的最大字节数，避免超出预期大小）
                        int readBytes = Math.min(buf.readableBytes(), (int) (fileTotalSize - receivedSize));
                        if (readBytes > 0) {
                            // 读取ByteBuf中的数据（Netty推荐使用readBytes而非array()，避免内存泄漏）
                            byte[] data = new byte[readBytes];
                            buf.readBytes(data);
                            // 写入文件（同步写入，若需异步可结合Netty的FileRegion或自定义线程池）
                            file.write(data);
                            // 更新CRC与接收进度
                            crc32.update(data);
                            receivedSize += readBytes;

                            // 打印接收进度（每接收10%更新一次）
                            int progress = (int) ((receivedSize * 100.0) / fileTotalSize);
                            if (progress % 10 == 0 && progress != (int) (((receivedSize - readBytes) * 100.0) / fileTotalSize)) {
                                System.out.println("文件接收进度：" + progress + "%（" + receivedSize + "/" + fileTotalSize + "字节）");
                            }
                        }

                        // 3. 接收完成：校验CRC并反馈结果
                        if (receivedSize == fileTotalSize) {
                            long actualCrc = crc32.getValue();
                            String resultMsg;
                            if (actualCrc == expectedCrc) {
                                resultMsg = "SUCCESS: 文件接收完成，保存路径：" + saveDir + fileName + "，实际大小：" + receivedSize + "字节，CRC校验通过";
                                System.out.println(resultMsg);
                            } else {
                                resultMsg = "ERROR: 文件接收完成但CRC校验失败（预期：" + expectedCrc + "，实际：" + actualCrc + "），已删除损坏文件";
                                // 删除损坏文件
                                if (file != null) file.close();
                                new File(saveDir + fileName).delete();
                                System.err.println(resultMsg);
                            }
                            // 向客户端发送结果
                            ctx.writeAndFlush(ctx.alloc().buffer().writeBytes(resultMsg.getBytes(StandardCharsets.UTF_8)));
                            // 重置状态，准备接收下一个文件（可选，若支持多文件连续传输）
                            resetFileState();
                        }
                        break;
                }
            }
        } finally {
            // 释放ByteBuf资源（Netty关键：避免内存泄漏，必须调用）
            buf.release();
        }
    }

    /**
     * 重置文件接收状态（用于多文件连续传输场景）
     */
    private void resetFileState() {
        fileStage = 0;
        fileNameLen = 0;
        fileName = null;
        fileTotalSize = 0;
        expectedCrc = 0;
        receivedSize = 0;
        // 关闭文件流（若已打开）
        if (file != null) {
            try {
                file.close();
            } catch (Exception e) {
                System.err.println("关闭文件流异常：" + e.getMessage());
            }
            file = null;
        }
        crc32 = null;
    }

    /**
     * 连接关闭时调用：确保资源彻底释放
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端连接关闭：" + ctx.channel().remoteAddress());
        resetFileState(); // 释放文件相关资源
        super.channelInactive(ctx);
    }

    /**
     * 发生异常时调用：关闭连接并释放资源
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        String errorMsg = "文件接收异常（客户端：" + ctx.channel().remoteAddress() + "）：" + cause.getMessage();
        System.err.println(errorMsg);
        // 向客户端发送异常信息
        ctx.writeAndFlush(ctx.alloc().buffer().writeBytes(errorMsg.getBytes(StandardCharsets.UTF_8)));
        // 释放资源并关闭连接
        resetFileState();
        ctx.close();
    }
}